using System;
using System.Collections.Generic;
using System.Reflection;
using Kopernikus.Parsing.Lexing;

namespace Kopernikus.Parsing.GenericActions
{
    public abstract class LexerActionBase<TLexer, TToken> : ILexerAction<TLexer, TToken>
    {
        private readonly ActionDelegate m_actionDelegate;
        private readonly StaticActionDelegate m_staticActionDelegate;

        public LexerActionBase(MethodInfo methodInfo)
        {
            try
            {
                if (methodInfo.IsStatic)
                {
                    m_staticActionDelegate =
                        Delegate.CreateDelegate(typeof (StaticActionDelegate), methodInfo) as StaticActionDelegate;
                }
                else
                {
                    m_actionDelegate =
                        Delegate.CreateDelegate(typeof (ActionDelegate), null, methodInfo) as
                        ActionDelegate;
                }
            }
            catch (ArgumentException)
            {
                throw new InvalidLexerActionException(methodInfo, typeof (TToken));
            }
        }

        #region ILexerAction<TLexer,TToken> Members

        public Symbol GetSymbol(string value, Dictionary<string, Symbol> symbolCache)
        {
            return MapSymbol(value, symbolCache);
        }

        public bool CanInvoke
        {
            get { return (m_actionDelegate != null) || (m_staticActionDelegate != null); }
        }

        public TToken Invoke(TLexer instance, LexerMatch m)
        {
            return m_actionDelegate == null ? m_staticActionDelegate(m) : m_actionDelegate(instance, m);
        }

        #endregion

        protected abstract Symbol MapSymbol(string value, Dictionary<string, Symbol> symbolCache);

        #region Nested type: ActionDelegate

        private delegate TToken ActionDelegate(TLexer instance, LexerMatch m);

        #endregion

        #region Nested type: StaticActionDelegate

        private delegate TToken StaticActionDelegate(LexerMatch m);

        #endregion
    }
}